package me.haoliang

import akka.actor.SupervisorStrategy.{Restart, Stop}
import akka.actor.{Actor, ActorInitializationException, ActorKilledException, ActorSystem, DeathPactException, OneForOneStrategy, Props, SupervisorStrategy, Terminated}
import akka.event.Logging

/**
  * Created by pulangke on 2017/3/1.
  */
class SimpleAkkaDemo extends Actor {


  import context._
  import scala.concurrent.duration._

  /**
    * 可以使用context.unwatch(target)来停止对另一个actor生存状态的监控。即使Terminated已经加入邮箱，该操作仍有效；
    * 一旦调用unwatch，则被观察的actor的Terminated消息就都不会再被处理。
    * 所以unwatch 不影响stop对子actor的关闭
    */
  val logger = Logging(context.system, this);
  val child = actorOf(Props[ChildActor], name = "child")
  watch(child)

  @scala.throws[Exception](classOf[Exception])
  override def preStart(): Unit = {
    super.preStart()
    logger.info("start")
    /**
      * 远程调用
      * http://udn.yyuap.com/doc/akka-doc-cn/2.3.6/scala/book/chapter5/03_remoting.html
      */
    val selection = system.actorSelection("akka.tcp://mySystem@127.0.0.1:2552/user/simpleAkka/child")
    selection ! "remote"


  }


  /**
    * 它是一对一策略，意思是每一个子actor会被单独处理（多对一的策略与之相似，
    * 唯一的区别在于任何决策都应用于监管者的所有子actor，而不仅仅是出错的那一个）
    */
  override val supervisorStrategy =
    OneForOneStrategy(maxNrOfRetries = 5, withinTimeRange = 1 minute) {
      case _: ActorInitializationException => Stop
      case _: ActorKilledException => Stop
      case _: DeathPactException => Stop
      case _: Exception => Restart
    }

  override def receive = {
    case "stop" => {
      stop(self)
    }
    case "killChild" => {
      stop(child)
    }
    case Terminated(child) => {
      logger.info("child was killed")
    }

    case x: String => {
      logger.info(x)
      child ! x + "_child"
      //self ! "a"
    }


  }

  @scala.throws[Exception](classOf[Exception])
  override def postStop() = {
    logger.info("stop")
    super.postStop()
  }
}

class ChildActor extends Actor {

  val logger = Logging(context.system, this);

  /**
    * 在actor第一次创建时，将调用此方法。在重新启动期间，
    * 它被postRestart的默认实现调用，这意味着通过重写该方法，
    * 你可以选择是仅仅在初始化该actor时调用一次，还是为每次重
    * 新启动都调用。actor构造函数中的初始化代码将在每个actor
    * 实例创建的时候被调用，这也发生在每次重启时。
    */
  @scala.throws[Exception](classOf[Exception])
  override def preStart() = {
    logger.info("preStart")
  }

  @scala.throws[Exception](classOf[Exception])
  override def preRestart(reason: Throwable, message: Option[Any]) = {
    super.preRestart(reason, message)
    logger.info("restart")
  }

  override def receive = {
    case x: String => {
      logger.info(x)
    }
  }

  @scala.throws[Exception](classOf[Exception])
  override def postStop() = {
    //主方法停止的时候会优先让子actor停止
    logger.info("child Actor stop")
    super.postStop()
  }
}


object SimpleAkkaDemo {
  def main(args: Array[String]) = {
    val system = ActorSystem("mySystem")
    val simpleAkka = system.actorOf(Props[SimpleAkkaDemo], name = "simpleAkka")
    //simpleAkka ! "haha"
    Thread.sleep(1000)
    simpleAkka ! "killChild"
    //simpleAkka ! "stop"
  }
}
